#include <iostream>
#include <string>
#include <string.h>
#include <functional>
#include <memory>

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

const static int backlog = 8;

// TCP的套接字流程：socket -> fcntl设为非阻塞(可选) -> setsockopt设置端口号可复用(可选)
// 准备服务器的IP和port + bind -> listen -> accept -> 将新连接的文件描述符用fcntl设为非阻塞(可选)
// ->得到客户端的IP和port -> recv -> 反序列化 + 处理数据 -> 将处理结果send

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cout << "Usage: " << argv[0] << " Port" << endl;
        exit(-1);
    }

    uint16_t serverport = stoi(argv[1]);

    int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        exit(-1);
    }

    // fcntl设为非阻塞(可选)
    // int fl = ::fcntl(sockfd, F_GETFL);
    // if (fl < 0)
    // {
    //     cout << "fcntl error" << endl;
    // }
    // ::fcntl(sockfd, F_SETFL, fl | O_NONBLOCK);

    // setsockopt设置端口号可复用(可选)
    int opt = 1;
    ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 准备服务器的IP和port + bind
    struct sockaddr_in serveraddr;
    socklen_t len = sizeof(serveraddr);
    memset(&serveraddr, 0, len);
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(serverport);
    serveraddr.sin_addr.s_addr = INADDR_ANY;
    if (::bind(sockfd, (struct sockaddr *)&serveraddr, len) < 0)
    {
        cout << "bind error" << endl;
        exit(-1);
    }

    // listen
    if (::listen(sockfd, backlog) < 0)
    {
        cout << "listen error";
        exit(-1);
    }

    while (true)
    {
        errno = 0;
        // accept
        struct sockaddr_in tmp;
        socklen_t len = sizeof(tmp);
        int newsock = ::accept(sockfd, (struct sockaddr *)&tmp, &len);
        if (newsock >= 0)
        {
            // fcntl设为非阻塞(可选)
            int fl = ::fcntl(sockfd, F_GETFL);
            if (fl < 0)
            {
                cout << "fcntl error" << endl;
            }
            ::fcntl(sockfd, F_SETFL, fl | O_NONBLOCK);

            // 得到客户端的IP和port
            uint16_t clientport = ntohs(tmp.sin_port);
            string clientip = inet_ntoa(tmp.sin_addr);
            cout << "A new client want to connect " << clientip << " " << to_string(clientport) << endl;

            // recv
            char inbuffer[1024] = {0};
            int m = ::recv(newsock, inbuffer, sizeof(inbuffer) - 1, 0);
            if (m > 0)
            {
                // 处理数据(反序列化)
                inbuffer[m] = 0;
                string echo_string = clientip;
                echo_string += " ";
                echo_string += to_string(clientport);
                echo_string += " ";
                echo_string += inbuffer;
                cout << echo_string << endl;

                // 将处理结果send
                ::send(newsock, echo_string.c_str(), echo_string.size(), 0);
            }
            else
            {
                break;
            }
        }
        else
        {
            if (errno == EWOULDBLOCK)
            {
                cout << "全部链接都已获取!" << endl;
                continue;
            }
            else if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
    }

    return 0;
}